﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NHibernate;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;

namespace PinCity.ORM
{
    /// <summary>
    /// Static class, that is responsible for Fluent NHibernate
    /// configuration and session creation. Must be used only in
    /// classes, implementing PinCity.Models.Repositories.IRepository
    /// </summary>
    public static class FluentManager
    {
        private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(typeof(FluentManager));
        /// <summary>
        /// Internal dummy object, used to synchronize FluentManager behavior
        /// during initialization.
        /// </summary>
        private static object _syncRoot = new object();

        /// <summary>
        /// Internal field, that stores session factory. It is used to create
        /// and return sessions from manager.
        /// </summary>
        private static ISessionFactory _sessionFactory = null;

        /// <summary>
        /// Internal field, that stores databse configuration.
        /// </summary>
        private static IPersistenceConfigurer _databaseConfig = null;

        /// <summary>
        /// Gets or sets database configuration. Usually a *Configuration object 
        /// from FluentNHibernate.Cfg.Db namespace with initialized connection string.
        /// This property should be set before call of FluentManager.Initialize() method.
        /// </summary>
        public static IPersistenceConfigurer DatabaseConfig { get { return _databaseConfig; } set { FluentManager._databaseConfig = value; } }

        /// <summary>
        /// Gets session factory. Avoid usage of this property, unless really needed.
        /// </summary>
        public static ISessionFactory SessionFactory { get { return FluentManager._sessionFactory; } }
        /// <summary>
        /// Initialize FluentManager. Will create database schema.
        /// The FluentManager.DatabaseConfig property should be set to correct value 
        /// before call to this method.
        /// </summary>
        public static void Initialize()
        {
            FluentManager.Initialize(true);
        }

        /// <summary>
        /// Initialize FluentManager. The FluentManager.DatabaseConfig 
        /// property should be set to correct value before call to this method.
        /// </summary>
        /// <param name="createSchema">Indicates, whether the creation of tables is needed.</param>
        public static void Initialize(bool createSchema)
        { 
            lock (FluentManager._syncRoot)
            {
                if (FluentManager._sessionFactory == null) // if not initialized
                {
                    FluentManager._sessionFactory = Fluently.Configure() // start configuration
                    .Database(FluentManager.DatabaseConfig) // set database configuration
                    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<MvcApplication>()) // add mappings from current assembly
                    .ExposeConfiguration(cfg =>
                        {
                            cfg.SetProperty("current_session_context_class", "managed_web");
                            if (createSchema) // if need to recreate schema
                            {
                                new SchemaExport(cfg).Create(false, true); // recreate schema
                            }
                        }
                    )
                    .BuildSessionFactory(); // finally, create session factory and store it in the internal field
                }
            }
        }

        /// <summary>
        /// Returns a session, that is bound to current session context (HttpContext).
        /// </summary>
        /// <returns>Current session.</returns>
        public static ISession GetCurrentSession()
        {
            return FluentManager._sessionFactory.GetCurrentSession();
        }

        /// <summary>
        /// Returns a transaction for current session.
        /// Starts a transaction, if there is no one.
        /// </summary>
        /// <returns>Current transaction.</returns>
        public static ITransaction GetCurrentTransaction()
        {
            ITransaction result = FluentManager.GetCurrentSession().Transaction;
            if ( result == null)
            {
                result = FluentManager.GetCurrentSession().BeginTransaction();
            }
            return result;
        }

        /// <summary>
        /// Returns brand new session object. Must be used only to
        /// tie current request with a session.
        /// </summary>
        /// <returns>New session.</returns>
        public static ISession OpenNewSession()
        {
            return FluentManager._sessionFactory.OpenSession();
        }
       
    }
}